use std::{env, fmt};
use std::sync::RwLock;

use anyhow::Context;
use base64::engine::{GeneralPurpose, GeneralPurposeConfig};
use chrono::Local;
use crypto::blockmodes::PkcsPadding;
use crypto::{aes::cbc_decryptor, digest::Digest};
use crypto::buffer::{ReadBuffer, RefReadBuffer, RefWriteBuffer, WriteBuffer};
use crypto::aes::{cbc_encryptor, KeySize::KeySize256};
use crypto::sha1::Sha1;
use log::info;
use base64::Engine;
use base64::alphabet::STANDARD;
use serde::{Deserialize, Serialize};
use serde_json::json;
use ysbase::func::{generate_random_string, generate_random_numeric_string};


// （修正解码失败问题）
const G: GeneralPurpose = GeneralPurpose::new(
    &STANDARD,
    GeneralPurposeConfig::new().with_decode_allow_trailing_bits(true),
);


const VALID_PERIOD: i64 = 10; // 有效期10秒

// 消息类型
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MessageType {
    Text,
    Image,
    Voice,
    Video,
    ShortVideo,
    Location,
    Link,
}

// 文本消息
#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct TextMessage {
    pub to_user_name: String,
    pub from_user_name: String,
    pub create_time: i64,
    pub msg_type: MessageType,
    pub content: String,
    pub msg_id: i64,
    pub msg_data_id: Option<String>,
    pub idx: Option<String>,
}

// 图片消息
pub struct ImageMessage {
    pub to_user_name: String,
    pub from_user_name: String,
    pub create_time: i64,
    pub msg_type: MessageType,
    pub pic_url: String,
    pub media_id: String,
    pub msg_id: i64,
    pub msg_data_id: Option<String>,
    pub idx: Option<String>,
}

// 语音消息
pub struct VoiceMessage {
    pub to_user_name: String,
    pub from_user_name: String,
    pub create_time: i64,
    pub msg_type: MessageType,
    pub media_id: String,
    pub format: String,
    pub msg_id: i64,
    pub msg_data_id: Option<String>,
    pub idx: Option<String>,
    pub media_id16_k: String,
}

// 视频消息
pub struct VideoMessage {
    pub to_user_name: String,
    pub from_user_name: String,
    pub create_time: i64,
    pub msg_type: MessageType,
    pub media_id: String,
    pub thumb_media_id: String,
    pub msg_id: i64,
    pub msg_data_id: Option<String>,
    pub idx: Option<String>,
}

// 小视频消息
pub struct ShortVideoMessage {
    pub to_user_name: String,
    pub from_user_name: String,
    pub create_time: i64,
    pub msg_type: MessageType,
    pub media_id: String,
    pub thumb_media_id: String,
    pub msg_id: i64,
    pub msg_data_id: Option<String>,
    pub idx: Option<String>,
}

// 地理位置消息
pub struct LocationMessage {
    pub to_user_name: String,
    pub from_user_name: String,
    pub create_time: i64,
    pub msg_type: MessageType,
    pub location_x: String,
    pub location_y: String,
    pub scale: i32,
    pub label: String,
    pub msg_id: i64,
    pub msg_data_id: Option<String>,
    pub idx: Option<String>,
}

// 链接消息
pub struct LinkMessage {
    pub to_user_name: String,
    pub from_user_name: String,
    pub create_time: i64,
    pub msg_type: MessageType,
    pub title: String,
    pub description: String,
    pub url: String,
    pub msg_id: i64,
    pub msg_data_id: Option<String>,
    pub idx: Option<String>,
}



pub trait ToXml {
    fn to_xml(&self) -> String;
}

// 文本响应
#[derive(Serialize)]
#[serde(rename_all = "PascalCase")]
#[serde(rename = "xml")]
pub struct TextResponse {
    pub to_user_name: String,
    pub from_user_name: String,
    pub create_time: i64,
    pub msg_type: MessageType,
    pub content: String,
}
impl TextResponse {
    pub fn new(to: &str, from: &str, content: &str) -> Self {
        Self { to_user_name: to.to_string(), from_user_name: from.to_string(), create_time: Local::now().timestamp(), msg_type: MessageType::Text, content: content.to_string() }
    }
}
impl ToXml for TextResponse {
    fn to_xml(&self) -> String {
        format!("<xml><ToUserName><![CDATA[{}]]></ToUserName><FromUserName><![CDATA[{}]]></FromUserName><CreateTime>{}</CreateTime><MsgType><![CDATA[{}]]></MsgType><Content><![CDATA[{}]]></Content></xml>", self.to_user_name, self.from_user_name, self.create_time, "text", self.content)
    }
}

#[derive(serde::Deserialize)]
#[serde(rename_all = "PascalCase")]
#[serde(rename = "xml")]
pub struct EncryptRequestData {
    pub to_user_name: String,
    pub encrypt: String,
}

#[derive(Serialize)]
#[serde(rename_all = "PascalCase")]
#[serde(rename = "xml")]
pub struct EncryptResponseData {
    pub encrypt: String,
    pub msg_signature: String,
    pub time_stamp: i64,
    pub nonce: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct WxResp {
    pub errcode: i32,
    pub errmsg: String,
}

#[derive(Debug)]
pub enum WxError {
    InvalidSignature,
    InvalidAppid,
    Internal(anyhow::Error),
}

impl fmt::Display for WxError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            WxError::InvalidSignature => write!(f, "Invalid signature"),
            WxError::InvalidAppid => write!(f, "Invalid appid"),
            WxError::Internal(e) => write!(f, "Internal error: {}", e),
        }
    }
}

impl<E> From<E> for WxError
where E: Into<anyhow::Error>,
{
    fn from(error: E) -> WxError {
        WxError::Internal(error.into())
    }
}


fn decrypt_msg(encrypt: &str, aes_key: &str, appid: &str) -> Result<String, WxError> {
    let aes_key  = G.decode(format!("{}=", aes_key))?;
    let encrypted_data = G.decode(encrypt)?;

    let iv = &aes_key[..16];
    let key = &aes_key[..32];

    let mut decryptor = cbc_decryptor(KeySize256, &key, &iv, PkcsPadding);
    let mut decrypted_data = vec![0; encrypted_data.len()];
    let mut read_buffer = RefReadBuffer::new(&encrypted_data);
    let mut write_buffer = RefWriteBuffer::new(&mut decrypted_data);
    decryptor.decrypt(&mut read_buffer, &mut write_buffer, true).unwrap();

    let decrypted_data = write_buffer.take_read_buffer().take_remaining().to_vec();

    // 解析解密后的数据
    let msg_len = u32::from_be_bytes(decrypted_data[16..20].try_into()?) as usize;
    let msg = String::from_utf8(decrypted_data[20..20 + msg_len].to_vec())?;
    let appid_decoded = String::from_utf8(decrypted_data[20 + msg_len..].to_vec())?;

    if appid_decoded != appid {
        return Err(WxError::InvalidAppid);
    }
    Ok(msg)
}

fn encrypt_msg(msg: &str, aes_key: &str, appid: &str, random_str: &str) -> Result<String, WxError> {
    let msg_len = (msg.len() as u32).to_be_bytes();
    let mut wtr = random_str.to_string().into_bytes();
    wtr.extend(msg_len);
    wtr.extend(msg.bytes());
    wtr.extend(appid.bytes());

    let aes_key = G.decode(format!("{}=", aes_key))?;

    
    let iv = &aes_key[..16];
    let key = &aes_key[..32];

    let mut encryptor = cbc_encryptor(KeySize256, &key, &iv, PkcsPadding);
    let mut encrypted_data = vec![0; wtr.len() + 32];
    let mut read_buffer = RefReadBuffer::new(&wtr);
    let mut write_buffer = RefWriteBuffer::new(&mut encrypted_data);
    encryptor.encrypt(&mut read_buffer, &mut write_buffer, true).unwrap();

    let encrypted_data = write_buffer.take_read_buffer().take_remaining().to_vec();
    let encrypted = G.encode(&encrypted_data);
    
    Ok(encrypted)
}

fn sha1_vec(params: Vec<&str>) -> String {
    let mut params = params.clone();
    params.sort();
    let mut hasher = Sha1::new();
    hasher.input_str(&params.concat());
    hasher.result_str()
}


#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AccessToken {
    #[serde(rename = "access_token")]
    pub token: String,      // 获取到的凭证
    pub expires_in: i64,    // 凭证有效时间，单位：秒
}


pub struct AccessTokenGetter {
    url: String,
    access_token: RwLock<Option<AccessToken>>,
}

impl AccessTokenGetter {
    pub fn new(url: String) -> Self {
        Self { url, access_token: RwLock::new(Option::None) }
    }

    pub async fn get_token(&self) -> Result<String, WxError> {
        let tmp = self.access_token.read().unwrap().clone();

        if let Some(token) = tmp {
            // 判断是否超时，如果未超时直接返回AccessToken里面的token
            let now = Local::now().timestamp();
            if token.expires_in > now {
                return Ok(token.token);
            }
        }

        let resp = reqwest::get(&self.url).await?;
        let text = resp.text().await?;
        let access_token: AccessToken = serde_json::from_str(&text).with_context(|| format!("get_access_token error: {}", text))?;
        
        let mut data = self.access_token.write().unwrap();
        *data = Option::Some(access_token.clone());

        Ok(access_token.token)
    }
}

#[derive(Clone)]
pub struct WechatMp {
    token: String,
    appid: String,
    secret: String,
    aes_key: String,
}
impl WechatMp {
    pub fn new(token: String, appid: String, secret: String, aes_key: String) -> Self {
        Self { token, appid, secret, aes_key }
    }

    pub fn new_from_env() -> Result<Self, WxError> {
        let appid = env::var("WECHAT_APPID").with_context(|| "WECHAT_APPID must be set!")?;
        let secret = env::var("WECHAT_SECRET").with_context(|| "WECHAT_SECRET must be set!")?;
        let token = env::var("WECHAT_TOKEN").with_context(|| "WECHAT_TOKEN must be set!")?;
        let aes_key = env::var("WECHAT_AES_KEY").with_context(|| "WECHAT_AES_KEY must be set!")?;
        let mp = Self { token, appid, secret, aes_key };
        Ok(mp)
    }

    pub fn check_signature(&self, signature: &str, timestamp: &str, nonce: &str) -> bool {
        // 检查时间戳是否在有效期内
        // let now = Local::now().timestamp();
        // if now - timestamp.parse::<i64>().unwrap() > VALID_PERIOD {
        //     return false;
        // }
        // 检查签名是否正确
        let token = &self.token;
        let params = vec![timestamp, nonce, token];
        let sign = sha1_vec(params);
        info!("check_signature sign: {}", sign);
        info!("check_signature signature: {}", signature);
        sign == signature
    }

    pub fn access_token_getter(&self) -> AccessTokenGetter {
        let url = format!("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}", self.appid, self.secret);
        AccessTokenGetter::new(url)
    }

    pub fn decrypt_message(&self, encrypt: &str, msg_signature: &str, timestamp: &str, nonce: &str) -> Result<String, WxError> {
        let token = &self.token;
        let params = vec![timestamp, nonce, encrypt, token];
        let sign = sha1_vec(params);
        if sign != msg_signature {
            return Err(WxError::InvalidSignature);
        }

        let msg = decrypt_msg(encrypt, &self.aes_key, &self.appid)?;
        Ok(msg)
    }

    pub fn encrypt_message<T>(&self, obj: &T) -> Result<String, WxError>
    where T: ToXml {
        let msg = obj.to_xml();

        let timestamp = Local::now().timestamp().to_string();
        let nonce = generate_random_numeric_string(8);
        let random = generate_random_string(16);
        info!("encrypt_message data: {:?}", vec![&msg, &timestamp, &nonce, &random]);

        // 加密
        let encrypt = encrypt_msg(&msg, &self.aes_key, &self.appid, &random)?;
        info!("encrypt_message encrypt: {}", encrypt);

        // 签名
        let msg_signature = sha1_vec(vec![&timestamp, &nonce, &encrypt, &self.token]);
        info!("encrypt_message msg_signature: {}", msg_signature);

        let resp = format!("<xml><Encrypt><![CDATA[{}]]></Encrypt><MsgSignature><![CDATA[{}]]></MsgSignature><TimeStamp>{}</TimeStamp><Nonce><![CDATA[{}]]></Nonce></xml>", encrypt, msg_signature, timestamp, nonce);
        info!("encrypt_message resp: {}", resp);
        Ok(resp)
    }

    pub async fn send_text_message(self, openid: &str, message: &str) -> Result<WxResp, WxError> {
        let token_getter = self.access_token_getter();
        let access_token = token_getter.get_token().await?;
        self.send_text_message_by_token(openid, message, &access_token).await
    }

    pub async fn send_text_message_by_token(self, openid: &str, message: &str, token: &str) -> Result<WxResp, WxError> {
        let url = format!("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={}", token);
        let client = reqwest::Client::new();
        let json_value = json!({
            "touser":openid,
            "msgtype":"text",
            "text":
            {
                "content": message
            }
        });
        let json_str = serde_json::to_string(&json_value).expect("Failed to serialize JSON");
        let resp = client.post(url)
            .body(json_str)
            .send()
            .await?;
        let wx_resp: WxResp = resp.json().await?;
        Ok(wx_resp)
    }
}



#[cfg(test)]
mod tests {
    use std::env;

    use dotenvy::dotenv;
    use async_trait::async_trait;
    use tokio::time::sleep;
    use ysbase::{func::str_to_timestamp, timer2::{TaskAction, Timer, TimerError, TimestampTrigger, Trigger}};

    use super::*;

    fn setup() {
        dotenv().ok();
    }

    
    #[tokio::test]
    async fn test_mp_timer() {
        setup();
        let timer = Timer::init().await.unwrap();

        #[derive(Clone)]
        struct MpSendTextAction {
            open_id: String,
            text: String,
            mp: WechatMp,
        }
        impl MpSendTextAction {
            fn new(open_id: &str, text: &str, mp: WechatMp) -> Self {
                Self {open_id: open_id.to_owned(), text: text.to_owned(), mp}
            }
        }
        #[async_trait]
        impl TaskAction for MpSendTextAction {
            type Output = ();
            async fn execute(&self) -> Result<Self::Output, TimerError> {
                let resp = self.mp.clone().send_text_message(&self.open_id, &self.text).await.unwrap();
                println!("wxresp: {:?}", resp);
                Ok(())
            }
        }

        let sec = str_to_timestamp("2024-11-24 09:43:00", "%Y-%m-%d %H:%M:%S");
        info!("sec: {}", sec);
        let mp = WechatMp::new_from_env().unwrap();
        let trigger = Trigger::Once(TimestampTrigger { timestamp: sec as u64 });
        let action = MpSendTextAction::new("oTxzgt4_WcFLCkzrxIKn7_L4YRzU", "测试123456", mp);
        let _ = timer.add_task(trigger, action).await;

        timer.start().await.unwrap();

        sleep(std::time::Duration::from_secs(180)).await;
    }


    
    #[tokio::test]
    async fn test_send_text() -> Result<(), WxError>{
        dotenv().ok();
        let appid = env::var("WECHAT_APPID").unwrap();
        let secret = env::var("WECHAT_SECRET").unwrap();
        let token = env::var("WECHAT_TOKEN").unwrap();
        let aes_key = env::var("WECHAT_AES_KEY").unwrap();

        let wechat_mp = WechatMp::new(token, appid, secret, aes_key);
        let resp = wechat_mp.send_text_message("oTxzgt4_WcFLCkzrxIKn7_L4YRzU", "55666").await?;
        println!("wxresp: {:?}", resp);
        Ok(())
    }

    #[test]
    fn test_check_signature() {
        let wechat_mp = WechatMp::new("token".to_string(), "appid".to_string(), "secret".to_string(), "aes_key".to_string());
        assert!(wechat_mp.check_signature("signature", "timestamp", "nonce"));
    }

    #[test]
    fn test_generate_random_string() {
        let random_string = generate_random_string(16);
        println!("random_string: {}", random_string);
        assert_eq!(random_string.len(), 16);
    }

    #[test]
    fn test_encrypt_message() {
        let aes_key = G.decode("AcftrLGpYBYY6k219eBrQlc2Wg6pYW4UMowdTSimDep=");
        match aes_key {
            Ok(aes_key) => {
                println!("aes_key: {:?}", aes_key);
            }
            Err(e) => {
                println!("Error decoding AES key: {}", e);
            }
        }
    }

    #[test]
    fn test_decrypt_msg() {
        dotenv().ok();
        let aes_key = env::var("WECHAT_AES_KEY").unwrap();
        let appid = env::var("WECHAT_APPID").unwrap();
        let encrypt = "/Boyfq5/+tl7e7HCxOMJNaQOW7i3j1W19zlWE0bD4nuBjyr7Cuc7cFL4qFPQiFC177lkMQXNm151ZWHZQ2R2zkHspxa1W17sa687wM8rhOJW8B2j5lQABrVxNyJWp47f5bMeZDsgAJ2EkzU3Sj+ehOYP8tQBG4ALdi2WUWr+Pny3EIx3zD6HwK02pK13D7iFSV6UPVcOwTDj7vTNsmL4+HJ6R4C1v3oA3XZ9ljCef1BLcP2rV8OeOoIZYMEr0H3a8kvbF7Gh21ff4ud+PRvn9cwZZWWstYaIV9OBAg1kmFY/fClV3I1G0M5HowkyFMkGrZlKiQLc5pdN7+XEklKnhlMJCpkxvq/9ot7tKtbrphc=";
        let msg = decrypt_msg(encrypt, &aes_key, &appid).unwrap();

        assert_eq!(msg, r#"<xml><ToUserName>oqu4_5zTAxul0Pvu3PtyoNhraFsU</ToUserName><FromUserName>gh_02c62aeccdb4</FromUserName><CreateTime>1729143549</CreateTime><MsgType>text</MsgType><Content>发生错误了，请联系管理员！</Content></xml>"#);
    }

    #[test]
    fn test_decrypt_msg2() {
        dotenv().ok();
        let aes_key = env::var("WECHAT_AES_KEY").unwrap();
        let appid = env::var("WECHAT_APPID").unwrap();
        let encrypt = "Vcib4e3mbGzDF0zHNQBbO49zlX3dMMcbOeXHpiq/sAX6Th9Ksk1HqLY06/reLQiRFQ6OkUaCsoAUqZMO/FrGZjVWjF3NsNK+qxGfopP0tOUb3jPqJCYKnB4v6//8CE/PYqkhTMp4+YG84aCbM3ySorZNVHlXL5ndTsYqGvH0hvZF+VTARNJWBtnpivmyljRPo8RszA8phJxXZcw6nncCjRuCyxf7lDCcpLc7ZvousKLHX2SqksIf2be9g/CkG+JoHW+Hq/eKyvxKRIwyfgEICp76cMo1BewW4aC5PAGj1uEWc7HZjqNvC34U14Y+P0nxdgrfYMkNn/a0z2lHz9mEYJIYFnjJxG9ki11vU6g0l1GvAcxrba8+0bI4WoqmkkzJRAReFWz4vRvAHElLw7TGcysS6/NnX22wcQmSMNGQEiU=";
        let msg = decrypt_msg(encrypt, &aes_key, &appid).unwrap();
        println!("msg: {}", msg);
    }

    #[test]
    fn test_encrypt_msg() {
        dotenv().ok();
        let aes_key = env::var("WECHAT_AES_KEY").unwrap();
        let appid = env::var("WECHAT_APPID").unwrap();
        let token = env::var("WECHAT_TOKEN").unwrap();

        let msg = r#"<xml><ToUserName><![CDATA[oqu4_51gsnxBwk9ffrns_uEURE0w]]></ToUserName><FromUserName><![CDATA[gh_02c62aeccdb4]]></FromUserName><CreateTime>1729149474</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[信息：你好]]></Content></xml>"#;
        let random = "2eb7b77273486492";
        let encrypt = encrypt_msg(msg, &aes_key, &appid, &random).unwrap();
        println!("encrypt: {}", encrypt);
        assert_eq!(encrypt, "qkju1VpYSTGEcrLM0Sz1AMdqYcrhOumLHfdUtepjt9ZzS6VGrR7AcaoVnJ6TJv21SPF1pTtlA8Rqs0VbtutyiGW6qh8lq6K9PalbIhH2/eySlvxzbhaWvXFVFd3MzmAyOSC6PzItacol2uuFHw2shDjn4DlfD9aL5sGF73T9Jfl51RfyTq0BP646dRYGT6JHxOcygl28IAH6CiJATGwgDdFGSskoQmEd8OlZprqOJZbRMy1twFwEBOemIxXS8L5Qsy/sjfkjRN/ACFTJzF5gpKLeWcypr2ckxq3OQXAHD3+k1PwZaUD7kzdNJmGcxAsEJjRIRdxwOpUiH4i7xP/UDQRwVnAu0++sgfkm+pebs6id4AhPhkorXYSMXFA0EKQy");

        let timestamp = "1729149474";
        let nonce = "57064896";
        let msg_signature = sha1_vec(vec![&timestamp, &nonce, &encrypt, &token]);
        assert_eq!(msg_signature, "ef407fe2aa1390990a454313d61e2842e79288ee");
    }

    #[test]
    fn test_encrypt_msg2() {
        dotenv().ok();
        let aes_key = env::var("WECHAT_AES_KEY").unwrap();
        let appid = env::var("WECHAT_APPID").unwrap();
        let token = env::var("WECHAT_TOKEN").unwrap();

        let msg = r#"<xml><ToUserName><![CDATA[oqu4_51gsnxBwk9ffrns_uEURE0w]]></ToUserName><FromUserName><![CDATA[gh_02c62aeccdb4]]></FromUserName><CreateTime>1729149474</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[信息：你好你好]]></Content></xml>"#;
        let random = "f6eebdcdbe1411ea";
        let encrypt = encrypt_msg(msg, &aes_key, &appid, &random).unwrap();
        println!("encrypt: {}", encrypt);
        assert_eq!(encrypt, "Vcib4e3mbGzDF0zHNQBbO49zlX3dMMcbOeXHpiq/sAX6Th9Ksk1HqLY06/reLQiRFQ6OkUaCsoAUqZMO/FrGZjVWjF3NsNK+qxGfopP0tOUb3jPqJCYKnB4v6//8CE/PYqkhTMp4+YG84aCbM3ySorZNVHlXL5ndTsYqGvH0hvZF+VTARNJWBtnpivmyljRPo8RszA8phJxXZcw6nncCjRuCyxf7lDCcpLc7ZvousKLHX2SqksIf2be9g/CkG+JoHW+Hq/eKyvxKRIwyfgEICp76cMo1BewW4aC5PAGj1uEWc7HZjqNvC34U14Y+P0nxdgrfYMkNn/a0z2lHz9mEYJIYFnjJxG9ki11vU6g0l1GvAcxrba8+0bI4WoqmkkzJRAReFWz4vRvAHElLw7TGcysS6/NnX22wcQmSMNGQEiU=");

        let timestamp = "1729149474";
        let nonce = "57064896";
        let msg_signature = sha1_vec(vec![&timestamp, &nonce, &encrypt, &token]);
        assert_eq!(msg_signature, "b95861263c21153aaff78b154321fef0af7da0ed");
    }


}
